/**
 * mLibVec2D.js
 * Created: April 2013
 * Source link: http://code.google.com/p/code-depository/source/browse/
 *
 * By Suresh Nalluri,
 * License: The MIT License (MIT)
 *          See LICENSE details
 *
 * Requires:
 *	mLibFactory.js
 *	mLibIncludes.js
 *
 *	Todo:
 *		1. may have to create a new Class for Point2 as we have no way of distinguishing
 *		  a point from vector.
 *
 *
 * 2D Vector Class
 * A 2D Vector is composed of x and y components
 *
 *
 *
 * note this constructor pattern allows us to create objects in 2 ways:
 * 1. var o = mLib.Vec2(x, y);      //OR
 * 2. var o = new mLib.Vec2(x,y);
 *
 * NOTE: var o = [new] mLIB.Vec2(); // will return default vector (1,0);
 *
 * added benefit of this pattern is that we get a copy constructor by default!!
 * eg. var newVec = mLIB.Vec2(existingVec);
 * so no need to have an explicit copyOf method.
 *
 * Usage: 
 *		var o = [new] mLIB.Vec2([ x,y | Vec2 | [] ])
 *
 *-------------------------------------------------------
 **/

if (typeof mLIB == 'undefined') function mLIB() {};

( function(module) {
	mLIB.Vec2 = Factory.obj( {

		init: function() {
			var nargs = arguments.length;


			if ( nargs == 1 ) {
					this.x = arguments[0].x;
					this.y = arguments[0].y;
			} else if ( nargs == 2 ) {
				// called with x,y params.. assume !
                this.x = arguments[0];
                this.y = arguments[1];
            } else {
                // arguments given don't meet our criteria. set to default vector
                this.x = 1.0;
                this.y = 0.0;
            }
		},
		
		length: function() {
			var x = this.x,
				y = this.y;
			return mLIB.Includes.sqrt(x*x + y*y);
		},
		
		lengthSquared: function() {
			var x = this.x,
				y = this.y;
			return x*x+y*y;
		},

		// tests for equality
		isEqual: function(v) {
			// use our equalty function!
			return mLIB.Includes.isZero(this.x - v.x) && mLIB.Includes.isZero(this.y - v.y);
		},
		
		isNotEqual: function(v) {
			return !( mLIB.Includes.isZero(this.x - v.x) && mLIB.Includes.isZero(this.y - v.y) );
		},
		
		//test for zero vector
		isZero: function() {
			var x = this.x,
				y = this.y;
			return mLIB.Includes.isZero( x*x + y*y );
		},
		
		// test for unit vector
		isUnit: function() {
			var x = this.x,
				y = this.y;
			return mLIB.Includes.isZero( 1.0 - x*x - y*y );
		},

		// zero the elements of self that are close to zero.
		roundToZero: function() {
			if ( mLIB.Includes.isZero( this.x ) ) this.x = 0.0;
			if ( mLIB.Includes.isZero( this.y ) ) this.y = 0.0;
			return this;
		},

		// set the vector to the zero vector
		zero: function() {
			this.x = this.y = 0.0;
			return this;
		},

		// normalise self
		normalise: function() {
			var x = this.x,
				y = this.y,
				lsq = x*x + y*y;
			
			if ( mLIB.Includes.isZero(lsq) ) {
				this.zero();
			} else
			{
				var scale = 1 / mLIB.Includes.sqrt( lsq );
				this.x *= scale;
				this.y *= scale;
			}
			return this;
		},
		
		// add a vector to self
		plus: function(v) {
			this.x += v.x;
			this.y += v.y;
			return this;
		},

		//return a new vector made  of self + v
		Plus: function(v) {
			return new mLIB.Vec2(this.x+v.x, this.y+v.y);
		},
		
		// subtract vector from self
		minus: function(v) {
			this.x -= v.x;
			this.y -= v.y;
			return this;
		},
		
		//return a new vector made  of self - v
		Minus: function(v) {
			return new mLIB.Vec2(this.x-v.x, this.y-v.y);
		},
		
		// return the vector which is the negation of self
		Negation: function() {
			return new mLIB.Vec2(-this.x, -this.y);
		},
		
		// set the vector to the negation of self
		negation: function() {
			this.x = -this.x;
            this.y =  -this.y;
            return this;
		},
		
		// scale self - scalar multiplication
		scaleUp: function(scale) {
			this.x *= scale;
			this.y *= scale;
			return this;
		},
		
		// scale self and return as new vector - scalar multiplication
		ScaleUp: function(scale) {
			return new mLIB.Vec2(this.x*scale, this.y*scale);
		},

		// scale self - scalar division
		scaleDown: function(scale) {
			// if scale is zero, then an error is thrown up.  let the app handle that
			this.x /= scale;
			this.y /= scale;
			return this;
		},
		
		// scale self and return as new vector - scalar division
		ScaleDown: function(scale) {
			// if scale is zero, then an error is thrown up.  let the app handle that
			return new mLIB.Vec2(this.x/scale, this.y/scale);
		},
		
		// dot product of self with a vector
		dot: function(v) {
			return ( this.x*v.x + this.y*v.y );
		},
		
		// return the perpendicular vector to self
		Perp: function() {
			return new mLIB.Vec2( -this.y, this.x ); // returns the +ve rotated vec
		},
		
		// return the perp dot product by self
		perpDot: function(v) {
			return ( -this.y*v.x + this.x*v.y);
		},

		// copy/assignment methods for a vector
		copy: function(v) {
			if ( v !== this ) {
				this.x = v.x;
				this.y = v.y;
			}
			return this;
		},

		// old habits die hard!  let's have a copyOf method for old time's sake..
		Copy: function(v) {
			return new mLIB.Vec2(this.x, this.y);
		},
		
		// accessor and setter methods
		set:  function(x, y) { this.x=x; this.y=y; return this; },
		setX: function(x) { this.x = x; return this;},
		setY: function(y) { this.y = y; return this;},
		getX: function() { return this.x },
		getY: function() { return this.y },

		// return the vector as an array
		toArray: function() {
			return [this.x, this.y].join('');
		},
		
		//debug
		log: function() {
			return ['(', this.x, ',', this.y, ')'].join("");
		}

	} ); //mLIB.Vec2 = Factory.obj

	mLIB.Point2 = mLIB.Vec2;		// 2D point defined using the Vec2 class type for now.
									// se Todo note 1. above

	// some friend methods
	//
	var friends = {
		// returns copy of a vector
		Copy: function(v) {
			return new mLIB.Vec2(v.x, v.y);
		},
		
		// distance between 2 points
		distance: function(p0, p1) {
			var x = p0.x - p1.x,
				y = p0.y - p1.y;
			return mLIB.Includes.sqrt( x*x + y*y );
		},
		
		distanceSquared: function(p0, p1) {
			var x = p0.x - p1.x,
				y = p0.y - p1.y;
			return x*x + y*y;
		},
		
		// scaling a vector - multipliaction
		ScaleUp: function(scale, v) {
			return new mLIB.Vec2(v.x*scale, v.y*scale);
		},

		// scaling a vector - division
		ScaleDown: function(scale, v) {
			return new mLIB.Vec2(v.x/scale, v.y/scale);
		},

		// adding 2 vectors
		Plus: function(v1, v2) {
			return new mLIB.Vec2(v1.x+v2.x, v1.y+v2.y);
		},

		// subtracting vectors
		Minus: function(v1, v2) {
			return new mLIB.Vec2(v1.x-v2.x, v1.y-v2.y);
		},

		// dot product
		dot: function(v1, v2) {
			return (v1.x*v2.x + v1.y*v2.y);
		},
		
		// check for class type
		isClass: function(theObj, objClass) {
			return Factory.isClass(theObj, objClass);
		},
		
		// return class type of a given object
		getClass: function(theObj) {
			return Factory.getClass(theObj);
		},
		
		// set up our definition of 2d space with origin and basis vectors
		origin: new mLIB.Point2(0.0, 0.0),
		xAxis:	new mLIB.Vec2(1.0, 0.0),	// i
		yAxis:	new mLIB.Vec2(0.0, 1.0)		// j
		
	}; // friends
	
	for (var f in friends) {
		friends.hasOwnProperty(f) && (mLIB.Vec2[f] = friends[f]);
	};
	
} )();





